package com.yingxue.demo.shiro;

import com.alibaba.fastjson.JSON;
import com.yingxue.demo.constant.Constant;
import com.yingxue.demo.exception.BizException;
import com.yingxue.demo.exception.enums.UnauthorizedExceptionEnum;
import com.yingxue.demo.exception.enums.BaseExceptionEnum;
import com.yingxue.demo.exception.enums.ServerExceptionEnum;
import com.yingxue.demo.vo.resp.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @Package: com.company.demo.filter
 * @Author: Mr.Waves
 * @Date: 2020-03-12 19:54
 * @Description: 自定义用户认证过滤器
 **/
@Slf4j
public class CustomAccessControllerFilter extends AccessControlFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        log.info("请求方式：{}",request.getMethod());
        log.info("请求url：{}",request.getRequestURL().toString());
        String accessToken = request.getHeader(Constant.ACCESS_TOKEN);

        try {
            if (StringUtils.isEmpty(accessToken)) {
                throw new BizException(UnauthorizedExceptionEnum.TOKEN_MISSING);
            }
            CustomUsernamePasswordToken customUsernamePasswordToken = new CustomUsernamePasswordToken(accessToken);
            getSubject(servletRequest, servletResponse).login(customUsernamePasswordToken);
        } catch (BizException e) {
            customResponse(e.getExceptionEnum(), servletResponse);
            return false;
        } catch (AuthenticationException e) {
            if (e.getCause() instanceof BizException) {
                BizException exception = (BizException) e.getCause();
                customResponse(exception.getExceptionEnum(), servletResponse);
            } else {
                customResponse(UnauthorizedExceptionEnum.AUTHENTICATE_ERROR, servletResponse);
            }
            return false;
        } catch (Exception e) {
            customResponse(ServerExceptionEnum.UNKNOWN_EXCEPTION, servletResponse);
        }
        return true;
    }

    /*
     * @RestControllerAdvice & @ControllerAdvice只能监控到Controller层的异常，过滤器不在其作用域内，
     * 故过滤器必须自行处理其运行时异常
     */
    private void customResponse(BaseExceptionEnum exceptionEnum, ServletResponse response) {
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(exceptionEnum.getCode());
        resultVO.setMessage(exceptionEnum.getMessage());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        String json = JSON.toJSONString(resultVO);
        // servlet容器会在请求完成后自动关闭outputStream，所以这里不需要@Cleanup
        try {
            OutputStream out = response.getOutputStream();
            out.write(json.getBytes("UTF-8"));
            out.flush();
        } catch (IOException e) {
            log.error("customResponse响应异常：",e);
        }
    }
}
